home *** CD-ROM | disk | FTP | other *** search
/ Magnum One / Magnum One (Mid-American Digital) (Disc Manufacturing).iso / d20 / msgq160s.arc / EXECSWAP.ASM < prev    next >
Assembly Source File  |  1991-10-26  |  21KB  |  844 lines

  1. ;
  2. ; EXECSWAP.ASM
  3. ; Swap memory and exec another program
  4. ; Copyright (C) 1988 TurboPower Software
  5. ; May be used freely as long as due credit is given
  6. ; Modified by P.J. Muller from code in Dr. Dobbs
  7. ;
  8. ; Modifications:
  9. ;   o Save DS on Stack instead of reloading with SEG Data.
  10. ;   o Rewrote most of the remaining Pascal into Assembler
  11. ;   o Moved variables from Data segment to Code segment
  12. ;   o Use Get PSP Dos Function instead of PrefixSeg variable
  13. ;   o Removed Data segment totally
  14. ;   o Flush the swap file before Exec (but it still says open)
  15. ;   o Now creates unique filename and use SwapFileName as path prefix
  16. ;
  17. ; Note:  If a path is specified for SwapFileName, it must be a full path
  18. ; including a drive specifier and trailing backslash.  Otherwise, an
  19. ; empty string must be passed and the current drive and directory will be
  20. ; used.
  21. ;
  22. ; The code is now very language INdependant
  23. ;
  24. ; Reference: Dr. Dobbs Journal, April 1989
  25. ;
  26. ; Compile for Turbo Pascal:     TASM /dTP=1 EXECSWAP
  27. ; Compile for Turbo C:        TASM EXECSWAP
  28. ;
  29.  
  30. IF TP
  31. Code    SEGMENT WORD PUBLIC
  32. ELSE
  33. Code    SEGMENT WORD PUBLIC 'CODE'
  34. ENDIF
  35.  
  36.     ASSUME CS:Code, DS:NOTHING, ES:NOTHING, SS:NOTHING
  37.     PUBLIC ExecWithSwap, ShutdownExecSwap, InitExecSwap
  38.     PUBLIC BytesSwapped, EmsAllocated, FileAllocated
  39.  
  40. FileAttr    EQU 6            ; Swap file attr (hidden+system)
  41. EmsPageSize    EQU 16384        ; EMS page size
  42. FileBlockSize    EQU 32768        ; Swap file block size
  43. StkSize        EQU 128            ; Temp stack size
  44. lo        EQU (WORD PTR 0)    ; Convenient typecasts
  45. hi        EQU (WORD PTR 2)
  46. ofst        EQU (WORD PTR 0)
  47. segm        EQU (WORD PTR 2)
  48.  
  49. ; Variables in CS
  50.  
  51. EmsDevice    DB 'EMMXXXX0',0        ; Name of EMS device driver
  52. UsedEms        DB 0            ; 1 if swapping to EMS, 0 if to file
  53. BytesSwappedCS    DD 0            ; Bytes to move during a swap
  54. FileAllocatedF    DB 0            ; Was a file allocated?
  55. EmsAllocatedF    DB 0            ; Was EMS allocated?
  56. EmsHandle    DW 0            ; EMS handle
  57. FrameSeg    DW 0            ; Segment of EMS page window
  58. FIleHandle    DW 0            ; DOS file handle
  59. PrefixSegCS    DW 0            ; Segment of base of program
  60. Status        DW 0            ; ExecSwap status code
  61. LeftToSwap    DD 0            ; Bytes left to move
  62. SaveSP        DW 0            ; Original SP
  63. SaveSS        DW 0            ; Original SS
  64. PathPtr        DD 0            ; Pointer to program to execute
  65. CmdPtr        DD 0            ; Pointer to command line to execute
  66. ParasWeHave    DW 0            ; Paragraphs allocated to process
  67. CmdLine        DB 128 DUP(0)        ; Terminated command line passed to DOS
  68. Path        DB 64 DUP(0)        ; Terminated path name passed to DOS
  69. FileBlock1    DB 16 DUP(0)        ; FCB passed to DOS
  70. FileBlock2    DB 16 DUP(0)        ; FCB passed to DOS
  71. EnvironSeg    DW 0            ; Segment of environment for child
  72. CmdLinePtr    DD 0            ; Pointer to terminated command line
  73. FilePtr1    DD 0            ; Pointers to FCBs
  74. FilePtr2    DD 0
  75. TempStack    DB StkSize DUP(0)    ; Temp stack
  76. StackTop    LABEL WORD        ; Initial TOS
  77.  
  78. ; Macros
  79.  
  80. MovSeg        MACRO Dest, Src        ; MOV seg,seg
  81.     push Src
  82.     pop Dest
  83.         ENDM
  84.  
  85. MovMem        MACRO Dest, Src        ; MOV AX,mem;  MOV mem,AX
  86.     mov ax,Src
  87.     mov Dest,ax
  88.         ENDM
  89.  
  90. InitSwapCount    MACRO            ; Init counter for bytes to swap
  91.     MovMem LeftToSwap.lo, BytesSwappedCS.lo
  92.     MovMem LeftToSwap.hi, BytesSwappedCS.hi
  93.         ENDM
  94.  
  95. SetSwapCount    MACRO BlkSize        ; Return CX=bytes to move this block
  96.     LOCAL FullBlk            ; and reduce total bytes left to move
  97.     mov cx,BlkSize            ; Assume we'll write a full block
  98.     cmp LeftToSwap.hi,0        ; Is high word still non-zero?
  99.     jnz FullBlk            ; Jump if so
  100.     cmp LeftToSwap.lo,BlkSize    ; Low word still a block or more?
  101.     jae FullBlk            ; Jump if so
  102.     mov cx,LeftToSwap.lo        ; Otherwise, move what's left
  103. FullBlk:sub LeftToSwap.lo,cx        ; Reduce number left to move
  104.     sbb LeftToSwap.hi,0
  105.         ENDM
  106.  
  107. NextBlock    MACRO SegReg, BlkSize    ; Point SegReg to next block to move
  108.     mov ax,SegReg
  109.     add ax,BlkSize/16        ; Add paragraphs to next segment
  110.     mov segreg,ax            ; next block to move
  111.     mov ax,LeftToSwap.lo
  112.     or ax,LeftToSwap.hi        ; Bytes left to move?
  113.         ENDM
  114.  
  115. EmsCall        MACRO FuncAH        ; Call EMM and prepare to check result
  116.     mov ah,FuncAH
  117.     int 67h
  118.     or ah,ah            ; Error code in AH
  119.         ENDM
  120.  
  121. DosCallAH    MACRO FuncAH        ; Call DOS subfunction AH
  122.     mov ah,FuncAH
  123.     int 21h
  124.         ENDM
  125.  
  126. DosCallAX    MACRO FuncAX        ; Call DOS subfunction AX
  127.     mov ax,FuncAX
  128.     int 21h
  129.         ENDM
  130.  
  131. InitSwapFile    MACRO
  132.     mov bx,FileHandle        ; BX=handle of swap file
  133.     xor cx,cx
  134.     xor dx,dx            ; Start of file
  135.     DosCallAX 4200h            ; Dos File Seek
  136.         ENDM
  137.  
  138. HaltWithError    MACRO Level        ; Halt if non-recoverable error occurs
  139.     mov al,Level            ; Set errorlevel
  140.     DosCallAX 4Ch
  141.         ENDM
  142.  
  143. MoveFast    MACRO            ; Move CX bytes from DS:SI to ES:DI
  144.     cld                ; Forward
  145.     shr cx,1            ; Convert to words
  146.     rep movsw            ; Move the words
  147.     rcl cx,1            ; Get the odd byte, if any
  148.     rep movsb            ; Move it
  149.         ENDM
  150.  
  151. SetTempStack    MACRO            ; Switch to Temp stack
  152.     mov ax,OFFSET StackTop        ; Point to TOS
  153.     mov bx,cs            ; Temp stack in this code segment
  154.     cli
  155.     mov ss,bx
  156.     mov sp,ax
  157.     sti
  158.         ENDM
  159.  
  160. ; FUNCTION ExecWithSwap(Path, CmdLine :String) :Word;
  161. ; Saves BP and DS
  162.  
  163. ExecWithSwap    PROC FAR
  164.     push bp
  165.     mov bp,sp            ; set up stack frame
  166.  
  167.     push ds                ; Save DS
  168.  
  169. ; Move variables to CS where we can easily access them later
  170.  
  171.     mov Status,1            ; Assume failure
  172.     les di,[bp+6]            ; ES:DI -> CmdLine
  173.     mov CmdPtr.ofst,DI
  174.     mov CmdPtr.segm,ES        ; CmdPtr -> command line string
  175.     les di,[bp+10]            ; ES:DI -> Path
  176.     mov PathPtr.ofst,DI
  177.     mov PathPtr.segm,ES        ; PathPtr -> path to execute
  178.     mov SaveSP,sp            ; Save stack position
  179.     mov SaveSS,ss
  180.  
  181.     mov ah,81            ; Get our PSP (undocumented)
  182.     int 21h
  183.     mov PrefixSegCS,bx        ; Save it
  184.  
  185.     InitSwapCount            ; Init bytes LeftToSwap
  186.     
  187. ; Check for swapping to EMS or file
  188.  
  189.     cmp EmsAllocatedF,0        ; Check flag for EMS method
  190.     jz NotEms            ; Jump if EMS not used
  191.     jmp short WriteE        ; Swap to EMS
  192. NotEms:    cmp FileAllocatedF,0        ; Check flag for swap file method
  193.     jnz WriteF            ; Swap to file
  194.     jmp ESDone            ; Exit if no swapping method set
  195.  
  196. ; Write to swap file
  197.  
  198. WriteF:    MovSeg DS,CS            ; DS = CS
  199.     InitSwapFile            ; Seek to start of swap file
  200.     jnc EF0                ; Jump if success
  201.     jmp ESDone            ; Exit if error
  202. EF0:    SetSwapCount FileBlockSize    ; CX = bytes to write
  203.     mov dx,OFFSET FirstToSave    ; DS:DX -> start of region to save
  204.     DosCallAH 40h            ; File Write
  205.     jc EF1                ; Jump if write error
  206.     cmp ax,cx            ; All bytes written?
  207.     jz EF2                ; Jump if so
  208. EF1:    jmp ESDone            ; Exit if error
  209. EF2:    NextBlock DS,FileBlockSize    ; Point DS to next block to write
  210.     jnz EF0                ; Loop if bytes left to write
  211.  
  212. ; Flush the swap file
  213.  
  214.     DosCallAH 45h            ; Dup file handle BX
  215.     jc EF20                ; ignore close if error
  216.     mov bx,ax            ; BX = dup handle
  217.     DosCallAH 3Eh            ; Close dup handle
  218.  
  219. EF20:    mov UsedEms,0            ; Flag we used swap file for swapping
  220.     jmp short SwapDone        ; Done swapping out
  221.  
  222. ; Write to EMS
  223.  
  224. WriteE:    mov es,FrameSeg            ; ES -> page window
  225.     mov dx,EmsHandle        ; DX = handle of our EMS block
  226.     xor bx,bx            ; BX = initial logical page
  227.     MovSeg ds,cs            ; DS = CS
  228. EE0:    xor al,al            ; Physical page 0
  229.     EmsCall 44h            ; Map physical page
  230.     jz EE1                ; Jump if success
  231.     jmp ESDone            ; Exit if error
  232. EE1:    SetSwapCount EmsPageSize    ; CX = bytes to move
  233.     xor di,di            ; ES:DI -> base of EMS page
  234.     mov si,OFFSET FirstToSave    ; DS:SI -> region to save
  235.     MoveFast            ; Move CX bytes from DS:SI to ES:DI
  236.     inc bx                ; Next logical page
  237.     NextBlock ds,EmsPageSize    ; Point ds to next page to move
  238.     jnz EE0                ; Loop if bytes left to move
  239.     mov UsedEms,1            ; Flag we used EMS for swapping
  240.  
  241. ; Shrink memory allocated to this process
  242.  
  243. SwapDone:mov ax,PrefixSegCS
  244.     mov es,ax            ; ES = segment of our memory block
  245.     dec ax
  246.     mov ds,ax            ; DS = segment of memory control block
  247.     mov cx,ds:[0003h]        ; CX = current paragraphs owned
  248.     mov ParasWeHave,cx        ; Save current paragraphs owned
  249.     SetTempStack            ; Switch to temporary stack
  250.     mov ax,OFFSET FirstToSave+15
  251.     mov cl,4
  252.     shr ax,cl            ; Convert offset to paragraphs
  253.     add bx,ax
  254.     sub bx,PrefixSegCS        ; BX = new paragraphs to keep
  255.     DosCallAH 4Ah            ; SetBlock
  256.     jnc EX0                ; Jump if successful
  257.     jmp EX5                ; Swap back and exit
  258.  
  259. ; Set up parameters and call DOS exec
  260.  
  261. EX0:    mov ax,es:[002ch]        ; Get environement segment
  262.     mov EnvironSeg,ax
  263.     MovSeg es,cs            ; ES = CS
  264.  
  265.     lds si,PathPtr            ; DS:SI -> path to execute
  266.     mov di,OFFSET Path        ; ES:DI -> local ASCIIZ copy
  267.     cld
  268.     lodsb                ; Read current length
  269.     cmp al,63            ; Truncate if exceeds space set aside
  270.     jb EX1
  271.     mov al,63
  272. EX1:    mov cl,al
  273.     xor ch,ch            ; CX = bytes to copy
  274.     rep movsb
  275.     xor al,al
  276.     stosb                ; ASCIIZ terminate
  277.  
  278.     lds si,CmdPtr            ; DS:SI -> Command line to pass
  279.     mov di,OFFSET CmdLine        ; ES:DI -> Local Terminated copy
  280.     lodsb                ; Read current length
  281.     cmp al,126            ; Truncate if exceeds space set aside
  282.     jb EX2
  283.     mov al,126
  284. EX2:    stosb
  285.     mov cl,al
  286.     xor ch,ch            ; CX = bytes to copy
  287.     rep movsb
  288.     mov al,0Dh
  289.     stosb                ; Terminate with ^M
  290.     MovSeg ds,cs            ; DS = CS
  291.     mov si,OFFSET CmdLine
  292.     mov CmdLinePtr.ofst, si
  293.     mov CmdLinePtr.segm, ds        ; Store pointer to command line
  294.     inc si
  295.  
  296.     mov di,OFFSET FileBlock1
  297.     mov FilePtr1.ofst, di
  298.     mov FilePtr1.segm, es        ; Store pointer to filename 1, if any
  299.     DosCallAX 2901h            ; Parse FCB
  300.  
  301.     mov di,OFFSET FileBlock2
  302.     mov FilePtr2.ofst, di
  303.     mov FilePtr2.segm, es        ; Store pointer to filename 2, if any
  304.     DosCallAX 2901h            ; Parse FCB
  305.  
  306.     mov dx,OFFSET Path
  307.     mov bx,OFFSET EnvironSeg
  308.     DosCallAX 4B00h            ; Exec
  309.     jc EX3                ; Jump if error in DOS call
  310.     xor ax,ax            ; return zero for success
  311. EX3:    mov Status,ax            ; save Dos error code
  312.  
  313. ; Set up temporary stack and reallocate original memory block
  314.  
  315.     SetTempStack
  316.     mov es,PrefixSegCS
  317.     mov bx,ParasWeHave
  318.     DosCallAH 4Ah            ; SetBlock
  319.     jnc EX4                ; Jump if no error
  320.     HaltWithError 0FFh        ; Must halt if failure here
  321. EX4:    InitSwapCount            ; Init LeftToSwap
  322.  
  323. ; Check which swap method is in use
  324.  
  325. EX5:    cmp UsedEms,0
  326.     jz ReadF            ; Jump to read back from file
  327.     jmp short ReadE            ; Read back from EMS
  328.  
  329. ; Read back from swap file
  330.  
  331. ReadF:    MovSeg ds,cs            ; DS = CS
  332.     InitSwapFile            ; Seek to start of swap file
  333.     jnc EF3                ; Jump if we succeeded
  334.     HaltWithError 0FEh        ; Must halt if failure here
  335. EF3:    SetSwapCount FileBlockSize    ; CX = bytes to read
  336.     mov dx,OFFSET FirstToSave    ; DS:DX -> start of region to restore
  337.     DosCallAH 3Fh            ; Read file
  338.     jnc EF4                ; Jump if no error
  339.     HaltWithError 0FEh        ; Must halt if failure here
  340. EF4:    cmp ax,cx
  341.     jz EF5                ; Jump if full block read
  342.     HaltWithError 0FEh        ; Must halt if failure here
  343. EF5:    NextBlock ds,FileBlockSize    ; Point DS to next page to read
  344.     jnz EF3                ; Jump if bytes left to read
  345.     jmp short ESDone        ; We're done
  346.  
  347. ; Copy back from EMS
  348. ReadE:    mov ds,FrameSeg            ; DS -> page window
  349.     mov dx,EmsHandle        ; DX = handle of our EMS block
  350.     xor bx,bx            ; BX = initial logical page
  351.     MovSeg es,cs            ; ES = CS
  352. EE3:    xor al,al            ; Physical page 0
  353.     EmsCall 44h            ; Map physical page
  354.     jz EE4                ; Jump if success
  355.     HaltWithError 0FDh        ; Must halt if failure here
  356. EE4:    SetSwapCount EmsPageSize    ; CX = Bytes to move
  357.     xor si,si            ; DS:SI -> base of EMS page
  358.     mov di,OFFSET FirstToSave    ; ES:DI -> region to restore
  359.     MoveFast            ; Move CX bytes from DS:SI to ES:DI
  360.     inc bx                ; Next logical page
  361.     NextBlock es, EmsPageSize    ; Point ES to next page to move
  362.     jnz EE3                ; Jump if so
  363.  
  364. ESDone:    cli                ; Switch back to original stack
  365.     mov ss,SaveSS
  366.     mov sp,SaveSP
  367.     sti
  368.     pop ds                ; Restore DS
  369.     mov ax,Status            ; Return status
  370.     pop bp
  371.     ret 8                ; Remove parameters and return
  372. ExecWithSwap    ENDP
  373.  
  374. ;-------------------------------------------------------------------------
  375.  
  376. ; Label EVEN marks first location to swap
  377. FirstToSave:
  378.  
  379. ; Local CS data which can be swapped out (PJM)
  380.  
  381. SwapName        DB 80 DUP(0)    ; ASCIIZ swap file name
  382.  
  383. ; FUNCTION AllocateSwapFile :Boolean;
  384.  
  385. AllocateSwapFile    PROC NEAR
  386.     mov cx,FileAttr            ; Attribute for swap file
  387.     push ds
  388.     MovSeg ds,cs
  389.     mov dx,OFFSET SwapName        ; DS:DX -> ASCIIZ swap name
  390.     DosCallAH 3Ch            ; Create file
  391.     pop ds
  392.     mov FileHandle,ax        ; Save handle assuming success
  393.     mov al,0            ; Assume failure
  394.     jc ASDone            ; Failed if carry set
  395.     inc al                ; Return true for success
  396. ASDone:    ret
  397. AllocateSwapFile    ENDP
  398.  
  399. ; PROCEDURE DeallocateSwapFile;
  400.  
  401. DeallocateSwapFile    PROC NEAR
  402.     push ds
  403.     mov bx,FileHandle        ; Handle of swap file
  404.     DosCallAH 3Eh            ; Close file
  405.     xor cx,cx            ; normal Attribute
  406.     MovSeg ds,cs
  407.     mov dx,OFFSET SwapName        ; DS:DX -> ASCIIZ swap name
  408.     DosCallAX 4301h            ; Set file attribute
  409.     DosCallAH 41h            ; Delete file
  410.     pop ds
  411.     ret
  412. DeallocateSwapFile    ENDP
  413.  
  414. ; FUNCTION EmsInstalled :Boolean;
  415.  
  416. EmsInstalled    PROC NEAR
  417.     push ds
  418.     MovSeg ds,cs            ; DS = CS
  419.     mov dx,OFFSET EmsDevice        ; DS:DX -> EMS driver name
  420.     DosCallAX 3D02h            ; Open for read/write
  421.     pop ds
  422.     mov bx,ax            ; Save handle in case one returned
  423.     mov al,0            ; Assume False
  424.     jc EIDone
  425.     DosCallAH 3Eh            ; Close file
  426.     mov al,1            ; Return True
  427. EIDone:    ret
  428. EmsInstalled    ENDP
  429.  
  430. ; FUNCTION EmsPageFrame :Word;
  431.  
  432. EmsPageFrame    PROC NEAR
  433.     EmsCall 41h            ; Get Page frame
  434.     mov ax,bx            ; AX = segment
  435.     jz EPDone            ; Done if Error = 0
  436.     xor ax,ax            ; else segment = 0
  437. EPDone:    ret
  438. EmsPageFrame    ENDP
  439.  
  440. ; FUNCTION AllocateEmsPages(NumPages :Word) :Word;
  441.  
  442. AllocateEmsPages    PROC NEAR
  443.     mov bx,sp            ; Set up stack frame
  444.     mov bx,ss:[bx+2]        ; BX = NumPages
  445.     EmsCall 43h            ; Allocate EMS
  446.     mov ax,dx            ; Assume success
  447.     jz APDone            ; Done if not 0
  448.     mov ax,0FFFFh            ; $FFFF for failure
  449. APDone:    ret 2                ; Remove parameter and return
  450. AllocateEmsPages    ENDP
  451.  
  452. ; PROCEDURE DeallocateEmsHandle(Handle :Word);
  453.  
  454. DeallocateEmsHandle    PROC NEAR
  455.     mov bx,sp            ; Set up stack frame
  456.     mov dx,ss:[bx+2]        ; DX = Handle
  457.     EmsCall 45h            ; Deallocate EMS
  458.     ret 2                ; Remove parameter and return
  459. DeallocateEmsHandle    ENDP
  460.  
  461. ; FUNCTION DefaultDrive :Char;
  462.  
  463. DefaultDrive    PROC NEAR
  464.     DosCallAH 19h            ; Get default drive
  465.     add al,'A'            ; Convert to character
  466.     ret
  467. DefaultDrive    ENDP
  468.  
  469. ; FUNCTION DiskFree(Drive :Byte) :LongInt;
  470.  
  471. DiskFree    PROC NEAR
  472.     mov bx,sp            ; Set up stack frame
  473.     mov dl,ss:[bx+2]        ; DL = Drive to check
  474.     DosCallAH 36h            ; Get disk space
  475.     mov dx,ax            ; Return 0FFFFFFFFh for failure
  476.     cmp ax,0FFFFh            ; Bad drive number?
  477.     jz DFDone            ; Jump if so
  478.     mul cx                ; AX = bytes/cluster
  479.     mul bx                ; DX:AX = bytes free
  480. DFDone:    ret 2                ; Remove parameter and return
  481. DiskFree    ENDP
  482.  
  483. ;
  484. ; The code that follows was added by PJM
  485. ;
  486.  
  487. ; PROCEDURE ShutdownExecSwap;
  488.  
  489. ShutdownExecSwap    PROC FAR
  490.     cmp EmsAllocatedF, 0        ; Was EMS allocated?
  491.     je SE1                ; no, check file
  492.  
  493. ; Deallocate EMS
  494.  
  495.     push EmsHandle            ; Parameter
  496.     call DeallocateEmsHandle
  497.     mov EmsAllocatedF, 0
  498.     jmp short SEDone
  499.  
  500. SE1:    cmp FileAllocatedF,0        ; Was a file allocated?
  501.     je SEDone            ; no, exit
  502.  
  503. ; Deallocate File
  504.  
  505.     call DeallocateSwapFile
  506.     mov FileAllocatedF,0
  507.  
  508. SEDone:    ret
  509.  
  510. ShutdownExecSwap    ENDP
  511.  
  512. ; FUNCTION Normalize(P :Pointer) :Pointer;
  513. ;
  514. ; 04d    P
  515. ; 02d    RetAddr (2)
  516. ; BP -> BP
  517.  
  518. Normalize    PROC NEAR
  519.     push bp
  520.     mov bp,sp
  521.  
  522.     mov ax,[bp+6]
  523.     xor dx,dx            ; DX:AX = Seg(P^)
  524. REPT 4
  525.     shl ax,1
  526.     rcl dx,1            ; DX:AX = DX:AX shl 4
  527. ENDM
  528.     add ax,[bp+4]
  529.     adc dx,0            ; DX:AX = DX:AX + Ofs(P^)
  530.  
  531.     pop bp
  532.     ret 4
  533. Normalize    ENDP
  534.  
  535. ; FUNCTION CanOpen(Name :ASCIIZ) :Boolean;
  536.  
  537. CanOpen    PROC NEAR
  538.     mov bx,sp            ; Stack frame
  539.     push ds
  540.  
  541.     lds dx,ss:[bx+2]
  542.     DosCallAX 3D00h            ; Open file for reading
  543.     jc CO1
  544.     mov bx,ax            ; handle
  545.     DosCallAH 3Eh            ; Close file
  546.     mov al,1            ; Can open file
  547.     jmp short CO2
  548. CO1:    xor al,al            ; Can't open file
  549.  
  550. CO2:    pop ds
  551.     ret 4
  552. CanOpen    ENDP
  553.  
  554. ; FUNCTION HexPrint(Dest :Pointer;  Val :Word) :Pointer;
  555. ;
  556. ; 04d    Dest (4)
  557. ; 02d    Val (2)
  558.  
  559. HexPrint    PROC NEAR
  560.     mov bx,sp            ; Stack frame
  561.  
  562.     les di,ss:[bx+4]
  563.     cld
  564.  
  565.     mov al,ss:[bx+3]
  566.     mov cl,4
  567.     shr al,cl
  568.     call Digit
  569.     mov al,ss:[bx+3]
  570.     and al,15
  571.     call Digit
  572.  
  573.     mov al,ss:[bx+2]
  574.     mov cl,4
  575.     shr al,cl
  576.     call Digit
  577.     mov al,ss:[bx+2]
  578.     and al,15
  579.     call Digit
  580.  
  581.     mov ax,di
  582.     mov dx,es
  583.     ret 6
  584.  
  585. Digit:    add al,'0'
  586.     cmp al,'9'
  587.     jbe D0
  588.     add al,'A'-'9'-1
  589. D0:    stosb
  590.     ret
  591.  
  592. HexPrint    ENDP
  593.  
  594. ; FUNCTION GetDirectory(Buf :Pointer) :Pointer
  595.  
  596. GetDirectory    PROC NEAR
  597.     mov bx,sp
  598.     push ds
  599.  
  600.     lds si,ss:[bx+2]
  601.     mov byte ptr [si],0
  602.     xor dl,dl
  603.     DosCallAH 47h            ; Get default directory
  604.  
  605.     MovSeg es,ds
  606.     mov di,si            ; ES:DI = DS:SI
  607.  
  608.     cld
  609.     mov cx,-1
  610.     xor al,al
  611.     repnz scasb
  612.     dec di
  613.     not cx
  614.     dec cx                ; Length of string
  615.     jz GD1                ; If 0, exit
  616.     mov al,'\'            ; Not root, append \
  617.     stosb
  618.  
  619. GD1:    mov ax,di
  620.     mov dx,es
  621.  
  622.     pop ds
  623.     ret 4
  624.  
  625. GetDirectory    ENDP
  626.  
  627. ; FUNCTION InitExecSwap(LastToSave :Pointer;  SwapFileName :String) :Boolean;
  628. ;
  629. ; 10d    LastToSave (4)
  630. ; 06d    ^SwapFileName (4)
  631. ; 02d    RetAddr (4)
  632. ; BP ->    BP
  633. ; 04d    ^Name (4)            ; Name part of path
  634. ; 08d    Value (4)            ; Timer value
  635.  
  636. NamePart    equ (dword ptr [bp-4])
  637. TimerValue    equ (dword ptr [bp-8])
  638.  
  639. InitExecSwap    PROC FAR
  640.     push bp
  641.     mov bp,sp            ; Stack frame
  642.     sub sp,8            ; Local variables
  643.  
  644.     mov al,EmsAllocatedF        ; EMS or File allocated?
  645.     or al,FileAllocatedF
  646.     jz IE1                ; no, continue
  647.     xor al,al            ; yes, exit (False)
  648.     jmp IEDone
  649.  
  650. ; Work out bytes to swap
  651.  
  652. IE1:    push [bp+12]            ; Seg(LastToSave^)
  653.     push [bp+10]            ; Ofs(LastToSave^)
  654.     call Normalize
  655.     mov [bp+12],dx
  656.     mov [bp+10],ax            ; LastToSave := Normalize(LastToSave)
  657.  
  658.     push cs                ; Seg(FirstToSave)
  659.     mov ax,OFFSET FirstToSave    ; Ofs(FirstToSave)
  660.     push ax
  661.     call Normalize            ; DX:AX = Normalize(FirstToSave)
  662.     xchg [bp+12],dx
  663.     xchg [bp+10],ax            ; LastToSave <=> DX:AX
  664.  
  665.     sub ax,[bp+10]
  666.     sbb dx,[bp+12]            ; DX:AX - LastToSave
  667.  
  668.     mov BytesSwappedCS.lo, ax
  669.     mov BytesSwappedCS.hi, dx    ; BytesSwappedCS = DX:AX
  670.  
  671.     or dx,dx
  672.     js IE20                ; Jump if Negative
  673.     or ax,dx
  674.     jnz IE2                ; Jump if not zero
  675. IE20:    xor al,al            ; Non-Positive amount, exit (False)
  676.     jmp IEDone
  677.  
  678. ; Check if EMS installed
  679.  
  680. IE2:    call EmsInstalled
  681.     or al,al            ; Installed?
  682.     jz IE3                ; No, do disk
  683.  
  684.     les ax,[BytesSwappedCS]
  685.     mov dx,es
  686.     add ax,EmsPageSize-1
  687.     adc dx,0            ; DX:AX = BytesSwappedCS+EmsPageSize-1
  688.     mov bx,EmsPageSize
  689.     div bx                ; AX = DX:AX / EmsPageSize
  690.  
  691.     push ax                ; AX = pages to allocate
  692.     call AllocateEmsPages
  693.     mov EmsHandle,ax        ; EmsHandle := AllocateEmsPages;
  694.  
  695.     cmp ax,0FFFFh            ; error?
  696.     je IE3                ; yes, do disk
  697.  
  698.     mov EmsAllocatedF, 1        ; EmsAllocatedF := True;
  699.  
  700.     call EmsPageFrame
  701.     mov FrameSeg,ax            ; FrameSeg := EmsPageFrame;
  702.  
  703.     or ax,ax
  704.     jz IE3                ; if 0, do disk
  705.  
  706.     mov al,1
  707.     jmp IEDone            ; Exit (True)
  708.  
  709. ; Do Disk swapping
  710.  
  711. ; Get a swap file name
  712.  
  713. IE3:    push ds                ; Save DS
  714.     lds si,[bp+6]            ; DS:SI -> SwapFileName
  715.     MovSeg es,cs
  716.     mov di,OFFSET SwapName        ; ES:DI -> SwapName
  717.     cld
  718.     lodsb                ; Get length
  719.     or al,al
  720.     jz IE34                ; No path specified
  721.     cmp al,79-14            ; Truncate if path too long
  722.     jb IE30    
  723.     mov al,79-14
  724. IE30:    cbw
  725.     mov cx,ax
  726.     rep movsb            ; Now ES:DI -> char past name
  727.     jmp short IE33
  728.  
  729. IE34:    call DefaultDrive        ; Get current directory
  730.     stosb                ; Store drive letter
  731.     mov al,':'
  732.     stosb
  733.     mov al,'\'
  734.     stosb                ; D:\
  735.  
  736.     push es
  737.     push di
  738.     call GetDirectory
  739.     mov es,dx
  740.     mov di,ax            ; ES:DI = GetDirectory(ES:DI)
  741.  
  742. IE33:    mov NamePart.lo,di
  743.     mov NamePart.hi,es        ; Save pointer to name
  744.  
  745.     xor ax,ax
  746.     mov ds,ax
  747.     mov ax,ds:[046Ch]        ; Get timer counter
  748.     mov TimerValue.lo,ax
  749.     mov ax,ds:[046Eh]
  750.     mov TimerValue.hi,ax        ; TimerValue := TimerCounter
  751.  
  752. IE32:    push NamePart.hi
  753.     push NamePart.lo
  754.     push TimerValue.hi
  755.     call HexPrint            ; DX:AX=HexPrint(NamePart, HiWord(TimerValue))
  756.     push dx
  757.     push ax
  758.     push TimerValue.lo
  759.     call HexPrint            ; DX:AX=HexPrint(DX:AX, LoWord(TimerValue))
  760.     mov es,dx
  761.     mov di,ax
  762.     mov al,'.'
  763.     stosb
  764.     mov al,'$'
  765.     stosb
  766.     stosb
  767.     stosb
  768.     xor al,al
  769.     stosb                ; _strcpy(DX:AX, ".$$$\0");
  770.  
  771.     push cs
  772.     mov ax,OFFSET SwapName
  773.     push ax
  774.     call CanOpen
  775.     or al,al
  776.     jz IE31                ; IF Not CanOpen(SwapName) goto ok
  777.  
  778.     add TimerValue.lo,1
  779.     adc TimerValue.hi,0        ; Inc(TimerValue)
  780.     jmp IE32            ; loop
  781.  
  782. IE31:    pop ds                ; Restore DS
  783.     MovSeg es,cs
  784.     mov di,OFFSET SwapName        ; ES:DI -> SwapName
  785.     cmp byte ptr es:[di+1],':'    ; Drive specified?
  786.     jne IE6                ; no drive specified
  787.  
  788.     mov al,es:[di]            ; Get drive letter
  789.     and al,not 32            ; Make upper case
  790.     jmp short IE7
  791.  
  792. IE6:    call DefaultDrive        ; AL = DefaultDrive
  793.  
  794. ; Now AL = Drive letter and SwapName is ASCIIZ swap file name
  795.  
  796. IE7:    sub al,40h            ; convert to number
  797.     push ax
  798.     call DiskFree            ; DX:AX = DiskFree(al)
  799.  
  800.     sub ax,BytesSwappedCS.lo
  801.     sbb dx,BytesSwappedCS.hi    ; DX:AX - BytesSwappedCS
  802.     jbe IE8                ; too little space, jump
  803.  
  804.     call AllocateSwapFile
  805.     or al,al
  806.     jz IE9                ; AL=0, failed
  807.  
  808.     mov al,1            ; AL=1, ok
  809.     jmp short IE9
  810.  
  811. IE8:    xor al,al
  812. IE9:    mov FileAllocatedF,al
  813.  
  814. IEDone:    mov sp,bp
  815.     pop bp                ; AL = Result
  816.     ret 8
  817.  
  818. InitExecSwap    ENDP
  819.  
  820. ; FUNCTION BytesSwapped :LongInt;
  821.  
  822. BytesSwapped    PROC FAR
  823.     les ax,BytesSwappedCS
  824.     mov dx,es
  825.     ret
  826. BytesSwapped    ENDP
  827.  
  828. ; FUNCTION EmsAllocated :Boolean;
  829.  
  830. EmsAllocated    PROC FAR
  831.     mov al,EmsAllocatedF
  832.     ret
  833. EmsAllocated     ENDP
  834.  
  835. ; FUNCTION FileAllocated :Boolean;
  836.  
  837. FileAllocated    PROC FAR
  838.     mov al,FileAllocatedF
  839.     ret
  840. FileAllocated    ENDP
  841.  
  842. Code    ENDS
  843.     END
  844.